﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

using Bricks.Component.Utils;
using System.Reflection;


namespace Bricks.Component.Data
{
    /// <summary>
    ///     EntityFramework仓储操作基类
    /// </summary>
    /// <typeparam name="TEntity">动态实体类型</typeparam>
    /// <typeparam name="TKey">实体主键类型</typeparam>
    public abstract class EFRepositoryBase<TEntity, TKey> : IRepository<TEntity, TKey> where TEntity : EntityBase<TKey>
    {
        #region 属性

        /// <summary>
        ///     获取 仓储上下文的实例
        /// </summary>
        [Import]
        public IUnitOfWork UnitOfWork { get; set; }

        /// <summary>
        ///     获取 EntityFramework的数据仓储上下文
        /// </summary>
        protected UnitOfWorkContextBase EFContext
        {
            get
            {
                if (UnitOfWork is UnitOfWorkContextBase)
                {
                    return UnitOfWork as UnitOfWorkContextBase;
                }
                throw new DataAccessException(string.Format("数据仓储上下文对象类型不正确，应为UnitOfWorkContextBase，实际为 {0}", UnitOfWork.GetType().Name));
            }
        }

        /// <summary>
        ///     获取 当前实体的查询数据集
        /// </summary>
        public virtual IQueryable<TEntity> Entities
        {
            get { return EFContext.Set<TEntity, TKey>(); }
        }

        #endregion

        #region 公共方法

        /// <summary>
        ///     插入实体记录
        /// </summary>
        /// <param name="entity"> 实体对象 </param>
        /// <param name="isSave"> 是否执行保存 </param>
        /// <returns> 操作影响的行数 </returns>
        public virtual int Insert(TEntity entity, bool isSave = true)
        {
            PublicHelper.CheckArgument(entity, "entity");
            EFContext.RegisterNew<TEntity, TKey>(entity);
            return isSave ? EFContext.Commit() : 0;
        }

        /// <summary>
        ///     批量插入实体记录集合
        /// </summary>
        /// <param name="entities"> 实体记录集合 </param>
        /// <param name="isSave"> 是否执行保存 </param>
        /// <returns> 操作影响的行数 </returns>
        public virtual int Insert(IEnumerable<TEntity> entities, bool isSave = true)
        {
            PublicHelper.CheckArgument(entities, "entities");
            EFContext.RegisterNew<TEntity, TKey>(entities);
            return isSave ? EFContext.Commit() : 0;
        }

        /// <summary>
        ///     删除指定编号的记录
        /// </summary>
        /// <param name="id"> 实体记录编号 </param>
        /// <param name="isSave"> 是否执行保存 </param>
        /// <returns> 操作影响的行数 </returns>
        public virtual int Delete(TKey id, bool isSave = true)
        {
            PublicHelper.CheckArgument(id, "id");
            TEntity entity = EFContext.Set<TEntity, TKey>().Find(id);
            return entity != null ? Delete(entity, isSave) : 0;
        }

        /// <summary>
        ///     删除实体记录
        /// </summary>
        /// <param name="entity"> 实体对象 </param>
        /// <param name="isSave"> 是否执行保存 </param>
        /// <returns> 操作影响的行数 </returns>
        public virtual int Delete(TEntity entity, bool isSave = true)
        {
            PublicHelper.CheckArgument(entity, "entity");
            EFContext.RegisterDeleted<TEntity, TKey>(entity);
            return isSave ? EFContext.Commit() : 0;
        }

        /// <summary>
        ///     删除实体记录集合
        /// </summary>
        /// <param name="entities"> 实体记录集合 </param>
        /// <param name="isSave"> 是否执行保存 </param>
        /// <returns> 操作影响的行数 </returns>
        public virtual int Delete(IEnumerable<TEntity> entities, bool isSave = true)
        {
            PublicHelper.CheckArgument(entities, "entities");
            EFContext.RegisterDeleted<TEntity, TKey>(entities);
            return isSave ? EFContext.Commit() : 0;
        }

        /// <summary>
        ///     删除所有符合特定表达式的数据
        /// </summary>
        /// <param name="predicate"> 查询条件谓语表达式 </param>
        /// <param name="isSave"> 是否执行保存 </param>
        /// <returns> 操作影响的行数 </returns>
        public virtual int Delete(Expression<Func<TEntity, bool>> predicate, bool isSave = true)
        {
            PublicHelper.CheckArgument(predicate, "predicate");
            List<TEntity> entities = EFContext.Set<TEntity, TKey>().Where(predicate).ToList();
            return entities.Count > 0 ? Delete(entities, isSave) : 0;
        }

        /// <summary>
        ///     更新实体记录
        /// </summary>
        /// <param name="entity"> 实体对象 </param>
        /// <param name="isSave"> 是否执行保存 </param>
        /// <returns> 操作影响的行数 </returns>
        public virtual int Update(TEntity entity, bool isSave = true)
        {
            PublicHelper.CheckArgument(entity, "entity");
            EFContext.RegisterModified<TEntity, TKey>(entity);
            return isSave ? EFContext.Commit() : 0;
        }

        /// <summary>
        /// 使用附带新值的实体信息更新指定实体属性的值
        /// </summary>
        /// <param name="propertyExpression">属性表达式</param>
        /// <param name="isSave">是否执行保存</param>
        /// <param name="entity">附带新值的实体信息，必须包含主键</param>
        /// <returns>操作影响的行数</returns>
        public int Update(Expression<Func<TEntity, object>> propertyExpression, TEntity entity, bool isSave = true)
        {
            throw new NotSupportedException("上下文公用，不支持按需更新功能。");
            PublicHelper.CheckArgument(propertyExpression, "propertyExpression");
            PublicHelper.CheckArgument(entity, "entity");
            EFContext.RegisterModified<TEntity, TKey>(propertyExpression, entity);
            if (isSave)
            {
                var dbSet = EFContext.Set<TEntity, TKey>();
                dbSet.Local.Clear();
                var entry = EFContext.DbContext.Entry(entity);
                return EFContext.Commit(false);
            }
            return 0;
        }

        /// <summary>
        /// 批量更新实体记录
        /// </summary>
        /// <param name="entities">要更新的实体集合</param>
        /// <param name="isSave">是否执行保存</param>
        /// <returns></returns>
        public virtual int Update(IEnumerable<TEntity> entities, bool isSave = true)
        {
            PublicHelper.CheckArgument(entities, "entities");
            EFContext.RegisterModified<TEntity, TKey>(entities);
            return isSave ? EFContext.Commit() : 0;
        }

        /// <summary>
        ///     查找指定主键的实体记录
        /// </summary>
        /// <param name="key"> 指定主键 </param>
        /// <returns> 符合编号的记录，不存在返回null </returns>
        public virtual TEntity GetByKey(TKey key)
        {
            PublicHelper.CheckArgument(key, "key");
            return EFContext.Set<TEntity, TKey>().Find(key);
        }

        public int ExeSqlCommon(string sqlStr)
        {
            return EFContext.ExeSqlCommon(sqlStr);
        }
        public IEnumerable<TEntity> SqlQueryCommon(string sql)
        {
            return EFContext.SqlQueryCommon<TEntity, TKey>(sql);
        }
        #endregion

        /// <summary>
        /// 设置排序的统一方法（向上向下）
        /// </summary>
        /// <param name="entities">涉及的实体集合</param>
        /// <param name="id">选中的记录ID</param>
        /// <param name="orderColumn">排序列（注意此列需非空，否则可能异常）</param>
        /// <param name="upOrDown">向上或向下</param>
        /// <returns></returns>
        public OperationResult SetOrder(IQueryable<TEntity> entities, TKey id, string orderColumn, string upOrDown)
        {
            TEntity model = GetByKey(id);  //根据ID取得记录

            PropertyInfo property = typeof(TEntity).GetProperty(orderColumn);  //取得排序的字段属性
            entities = EntitiesOrder(model, entities, orderColumn, upOrDown);  //对集合进行排序
            //var list = EntitiesPage(entities, 1, 1).ToList();
            var list = entities.Skip(0).Take(1).ToList();  //取得被排序影响的记录集合（实际上取第一个即可）

            bool success = false;  //是否成功

            if (list.Count > 0)
            {
                int temp = Convert.ToInt32(property.GetValue(model, null));
                property.SetValue(model, property.GetValue(list[0], null), null);
                property.SetValue(list[0], temp, null);
                list.Add(model);  //把要调整的记录加入集合

                success = Update(list) == 2;  //如果更新的是两个记录（调整的记录，和被调整的记录），则表示成功
                if (success)
                {
                    return new OperationResult(OperationResultType.Success, "调整成功");
                }
                else
                {
                    return new OperationResult(OperationResultType.Error, "调整失败");
                }
            }
            else
            {
                return new OperationResult(OperationResultType.Warning, "走到尽头啦");
            }
        }

        /// <summary>
        /// 设置实体排序
        /// </summary>
        /// <param name="model"></param>
        /// <param name="entities"></param>
        /// <param name="orderColumn"></param>
        /// <param name="upOrDown"></param>
        /// <returns></returns>
        public virtual IQueryable<TEntity> EntitiesOrder(TEntity model, IQueryable<TEntity> entities, string orderColumn, string upOrDown)
        {
            PropertyInfo property = typeof(TEntity).GetProperty(orderColumn);
            ParameterExpression parameter = Expression.Parameter(typeof(TEntity), "p");
            MemberExpression propertyAccess = Expression.MakeMemberAccess(parameter, property);
            LambdaExpression orderByExp = Expression.Lambda(propertyAccess, parameter);

            int temp = Convert.ToInt32(property.GetValue(model, null));
            ConstantExpression sort = Expression.Constant(temp, typeof(int));

            if (upOrDown.Equals("Up"))
            {
                BinaryExpression lessThan = Expression.LessThan(propertyAccess, sort);  //小于
                Expression<Func<TEntity, bool>> lambda = Expression.Lambda<Func<TEntity, bool>>(lessThan, parameter);

                entities = entities.Where(lambda);
                upOrDown = "OrderByDescending";
            }
            else
            {
                //因为OrderNo设置为可空，所以这里可能执行报错：没有为类型“System.Nullable`1[System.Int32]”和“System.Int32”定义二进制运
                //参考文章：http://www.cnblogs.com/wangjikun3/archive/2010/07/06/1772315.html

                BinaryExpression greaterThan = Expression.GreaterThan(propertyAccess, sort);
                Expression<Func<TEntity, bool>> lambda = Expression.Lambda<Func<TEntity, bool>>(greaterThan, parameter);

                entities = entities.Where(lambda);
                upOrDown = "OrderBy";
            }

            MethodCallExpression resultExp = Expression.Call(typeof(Queryable), upOrDown, new Type[] { typeof(TEntity), property.PropertyType }, entities.Expression, Expression.Quote(orderByExp));

            return entities.Provider.CreateQuery<TEntity>(resultExp);
        }

        /// <summary>
        /// 获取下一个排序号
        /// </summary>
        /// <param name="sortname">排序字段名称</param>
        /// <returns>最大值+1</returns>
        public virtual int GetNextOrderNo(Expression<Func<TEntity, int?>> sortname)
        {
            object max = Entities.Max(sortname);

            if (max != null)
            {
                return Convert.ToInt32(max) + 1;
            }
            else { return 1; }
        }

        /// <summary>
        /// 获取指定集合的下一个排序号
        /// </summary>
        /// <param name="entities">实体集合</param>
        /// <param name="sortname">排序字段名称</param>
        /// <returns>最大值+1</returns>
        public virtual int GetNextOrderNo(IQueryable<TEntity> entities, Expression<Func<TEntity, int?>> sortname)
        {
            object max = entities.Max(sortname);
            if (max != null)
            {
                return Convert.ToInt32(max) + 1;
            }
            else { return 1; }
        }

        /// <summary>
        /// 获取排序实体列表
        /// </summary>
        /// <param name="model">基础实体</param>
        /// <param name="entities">查询数据对象</param>
        /// <param name="sortname">排序字段</param>
        /// <param name="sortorder">排序方式</param>
        /// <returns>查询结果</returns>
        public virtual IQueryable<TEntity> GetOrderEntities(TEntity model, IQueryable<TEntity> entities, string sortname, string sortorder)
        {
            PropertyInfo property = typeof(TEntity).GetProperty(sortname);
            ParameterExpression parameter = Expression.Parameter(typeof(TEntity), "p");
            MemberExpression propertyAccess = Expression.MakeMemberAccess(parameter, property);
            LambdaExpression orderByExp = Expression.Lambda(propertyAccess, parameter);

            int temp = Convert.ToInt32(property.GetValue(model, null));
            ConstantExpression sort = Expression.Constant(temp, typeof(int));

            if (sortorder.Equals("up"))
            {
                BinaryExpression lessThan = Expression.LessThan(propertyAccess, sort);
                Expression<Func<TEntity, bool>> lambda = Expression.Lambda<Func<TEntity, bool>>(lessThan, parameter);

                entities = entities.Where(lambda);
                sortorder = "OrderByDescending";
            }
            else
            {
                BinaryExpression greaterThan = Expression.GreaterThan(propertyAccess, sort);
                Expression<Func<TEntity, bool>> lambda = Expression.Lambda<Func<TEntity, bool>>(greaterThan, parameter);

                entities = entities.Where(lambda);
                sortorder = "OrderBy";
            }

            MethodCallExpression resultExp = Expression.Call(typeof(Queryable), sortorder, new Type[] { typeof(TEntity), property.PropertyType }, entities.Expression, Expression.Quote(orderByExp));

            return entities.Provider.CreateQuery<TEntity>(resultExp);
        }
    }
}